# -*- coding: utf-8 -*-
# Clases para generar código Python.

## vipera, un diseñador de aplicaciones para Python
##    Copyright (C) 2011 Ángel Luis García García
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import textwrap
import StringIO

MAX_COLUMN_INFO = 65

def f_quitar_lineas_vacias(texto):
    '''Función que elimina las líneas vacías'''
    # Eliminamos las líneas vacías.
    texto_aux = StringIO.StringIO(texto)
    codigo = ''
    for i in texto_aux:
        if len(i.strip()) == 0: continue
        codigo += i
    # texto = codigo
    # Devolvemos texto.
    return codigo

class t_traductor(object):
    '''Clase base el traductor'''
    def __init__(self):
        '''Constuctor de clase'''
        self.indent = " " * 4
        self.enter = '\n'
        
class t_documentacion(t_traductor):
    '''Clase que genera la cabecera de documentación de un módulo'''
    def __init__(self, max_column = 65, dist_text = 13,
                 modulo = None, proposito = None, autor = None, 
                 creado = None, copyRight = None, licencia = None):
        '''Constructor de clase'''
        t_traductor.__init__(self)        
        self.__max_column = max_column
        self.__dis_text = dist_text
        self.__modulo = modulo
        self.__proposito = proposito
        self.__autor = autor
        self.__creado = creado
        self.__copyRight = copyRight
        self.__licencia = licencia

    def multilinea(self, texto, cabecera):
        '''Insertar comentarios en texto multilínea'''
        if texto is not None:
            if len(unicode(texto).strip()) == 0: return '# %s' % cabecera
            pre = u'# %s' % cabecera
            med = " " * (self.__dis_text - len(pre))
            pre += med
            seguir = True
            aux = list()
            lineas = textwrap.wrap(texto, self.__max_column)
            for i in lineas:
                if seguir:
                    aux.append(u'%s%s' % (pre, i))
                    seguir = False
                else:
                    aux.append(u'%s%s%s%s' % (self.enter, "#", " " * (self.__dis_text - 1), i))
            texto = ''
            for i in aux: texto += i
        else: texto = ''
        return texto

    def linea(self, texto, cabecera):
        '''Insertar comentario en línea de texto'''
        if texto is not None:
            if len(unicode(texto).strip()) == 0: return '# %s' % cabecera
            pre = u'# %s' % cabecera
            med = " " * (self.__dis_text - len(pre))
            pre += med
            texto = u'%s%s' % (pre, texto)
        else: texto = ''
        return texto

    def cabecera(self):
        '''Cabecera de módulo'''
        copyRight = self.multilinea(self.__copyRight, u"Copyright:")
        licencia = self.multilinea(self.__licencia, u"Licencia:")
        proposito = self.multilinea(self.__proposito, u"Propósito:")
        modulo = self.linea(self.__modulo, u"Módulo:")
        autor = self.linea(self.__autor, u"Autor:")
        creado = self.linea(self.__creado, u"Creado:")                        

        texto = '''#---------------------------------------------------------------------------
%s
%s
%s
%s
%s
%s
#---------------------------------------------------------------------------
''' % (modulo, proposito, autor, creado, copyRight, licencia)
        return texto

class t_codigo_principal(t_traductor):
    '''Clase que genera el código Python principal'''
    def __init__(self, indent, codigo = None):
        '''Constructor de clase'''
        t_traductor.__init__(self) 
        self.indent = " " * indent
        self.__codigo = codigo
        
    def codigo(self):
        '''Código de función Main()'''
        cabecera = u"def Main():%s" % self.enter
        # Si hay código Python, será el código principal.
        codigo = ''
        if self.__codigo is None: self.__codigo = ''
        # Hay que incluir la indentación.
        texto_aux = StringIO.StringIO(self.__codigo)
        for i in texto_aux: 
            codigo += u'%s%s' % (self.indent, i)
        # Si no hay código Python, el cuerpo es 'pass'
        if len(codigo.strip()) == 0:
            cuerpo = u'%s%s' % (self.indent, 'pass')
        else:
            cuerpo = codigo
        # Creamos el final del bloque.
        fin = u"%sMain()" % (self.enter * 2)
        # Reunimos todo.
        aux = u'# Función principal.%s' % self.enter
        aux2 = u'%s# Llamada a la función principal.%s' % (self.enter * 2, self.enter)
        ret = u'%s%s%s%s%s' % (aux, cabecera, cuerpo, aux2, fin)
        # Devolvemos el cuerpo.
        ret = f_quitar_lineas_vacias(ret)
        return ret

class t_funcion(t_traductor):
    '''Clase que genera código Python de una función''' 
    def __init__(self, indent, nombre, max_column = MAX_COLUMN_INFO, 
                 coment = None, pre = None, 
                 post = None, param = None, codigo = None):
        '''Constructor de clase'''
        t_traductor.__init__(self)        
        self.indent = " " * indent
        self.__nombre = nombre
        self.__max_column = max_column
        self.__coment = coment
        self.__pre = pre
        self.__post = post
        self.__param = param
        self.__codigo = codigo
        
    def cabecera(self):
        '''Método para construir la cabecera de la función'''
        # Identificador de elemento, esto es, una función.
        ident = "def"
        # Parámetros de la función.
        parametros = ''
        if self.__param is None: parametros = ''
        else:
            lista_param = []
            # param = [(nombre_param, valor_por_defecto|None, tipo_dato|None), ...]
            for i in self.__param:
                # Nombre del parámetro.
                nombre_param = i[0].strip()
                # Valor por defecto.
                if i[1] is not None: 
                    valor_por_defecto = i[1] # .strip()
                    # Creamos parámetro con inicialización.
                    k = u'%s = %s' % (nombre_param, valor_por_defecto)
                else:
                    # Creamos parámetro sin inicialización.
                    k = u'%s' % (nombre_param)
                # Añadimos el parámetro a la lista de parámetros.
                lista_param.append(k)
            # Creamos parámetros.
            for i in lista_param:
                if len(parametros) == 0: parametros += i
                else: parametros += '%s%s' % (', ',i)
        # Formamos la cabecera final.
        ret = u'%s %s(%s):' % (ident, self.__nombre, parametros)
        # Y la devolvemos.
        return ret

    def aserciones(self):
        '''Método para construir las aserciones de la función'''
        # Identificador de elemento, esto es, un assert.
        ident = "assert"
        ret = ''
        # Buscamos aserciones (comprobar los tipos en los parámetros de una función).
        if self.__param is not None: 
            lista_asserts = []
            # param = [(nombre_param, valor_por_defecto|None, tipo_dato|None), ...]
            for i in self.__param:
                # Nombre del parámetro.
                nombre_param = i[0]
                # Valor por defecto del parámetro.
                valor_por_defecto = i[1]
                # Comprobación de tipo de dato.
                tipo_dato = None if i[2] == '-' else i[2] 
                if tipo_dato is not None: 
                    # tipo_dato = i[2]
                    # Creamos asercion.
                    if valor_por_defecto == 'None':
                        j = u'%sif %s is not None: assert(type(%s)==%s)' % \
                        (self.indent, nombre_param, nombre_param, tipo_dato)
                    else: j = u'%sassert(type(%s)==%s)' % (self.indent, nombre_param, tipo_dato)                    
                    # Lo añadimos a la lista de aserciones.
                    lista_asserts.append(j)
            # Formamos el conjunto de asserts final.
            for i in lista_asserts:
                ret += u'%s%s' % (i, self.enter)
        # Y lo devolvemos.
        return ret
    
    def cuerpo(self):
        '''Método para construir el cuerpo de la función'''
        # Si hay código Python, será parte del cuerpo.
        codigo = ''
        if self.__codigo is not None:
            # Hay que incluir la indentación.
            texto_aux = StringIO.StringIO(self.__codigo)
            for i in texto_aux: 
                codigo += u'%s%s' % (self.indent, i)
        # Si hay asserts, será parte del cuerpo.
        asserts = self.aserciones()
        # Si no hay asserts ni código Python extra, el cuerpo es 'pass'
        if len(codigo.strip()) == 0 and len(asserts.strip()) == 0:
            cuerpo = u'%s%s' % (self.indent, 'pass')
        else:
            cuerpo = u'%s%s%s' % (asserts, self.enter, codigo)
        # Devolvemos el cuerpo.
        return cuerpo

    def informacion(self):
        '''Método para construir la documentación de la función'''        
        # Comentario.
        if self.__coment is not None:
            comentario = ''
            lineas = textwrap.wrap(self.__coment, self.__max_column)
            for i in lineas: comentario += u'%s%s%s' % (self.indent, i, self.enter)
        else: comentario = ''
        # Precondición.
        if self.__pre is not None:
            precondicion = ''
            lineas = textwrap.wrap(self.__pre, self.__max_column)
            for i in lineas: precondicion += u'%s%s%s' % (self.indent, i, self.enter)
        else: precondicion = ''
        # Postcondición.
        if self.__post is not None:
            postcondicion = ''
            lineas = textwrap.wrap(self.__post, self.__max_column)
            for i in lineas: postcondicion += u'%s%s%s' % (self.indent, i, self.enter)
        else: postcondicion = ''
        # Unión de comentario, precondición y postcondición.
        if len(comentario) == 0 and len(precondicion) == 0 and len(postcondicion) == 0:
            ret = ''
        else:
            triple_comillas = "\'\'\'"
            ret = u'%s%s%s%s%s%s%s%s' % (self.indent, triple_comillas, self.enter, 
                                         comentario, precondicion, postcondicion, 
                                         self.indent, triple_comillas)
        # Devolvemos la documentación.
        return ret
    
    def funcion(self):
        '''Método que devuelve el código de la función'''
        ret = ''
        # Datos...
        cabecera = self.cabecera()
        informacion = self.informacion()
        cuerpo = self.cuerpo()
        # Formamos el bloque.
        ret = u'# Definición de función %s.%s' % (self.__nombre, self.enter)
        ret += cabecera + self.enter
        if informacion is not None:
            ret += informacion + self.enter
        ret += cuerpo + self.enter
        # Eliminamos las líneas vacías.
        ret = f_quitar_lineas_vacias(ret)
        # Devolvemos texto.
        return ret

class t_constantes(t_traductor):
    '''Clase que genera código Python de constantes''' 
    def __init__(self, indent, lista_cte = None, lista_dict_cte = None): 
        '''Constructor de clase'''        
        t_traductor.__init__(self)        
        self.indent = " " * indent
        self.__lista_cte = lista_cte
        self.__lista_dict_cte = lista_dict_cte
        
    def constantes(self):
        '''Constantes'''
        # Creación de constantes: ["const1 = 'valor'", "const2 = 1"]
        const = ''
        if self.__lista_cte is None: return ''
        for i in self.__lista_cte:
            const += u'%s%s%s' % (self.indent, i, self.enter)
        # Devolvemos texto de constantes.
        return const
    
    def dict_constantes(self):
        '''Diccionario de constantes'''
        # Creación de diccionario de constantes: 
        # [(dict1,("const1 : 'valor'", ..., "const2 : 1")),
        # ...,
        # (dictN,(constK : 'valor23', ..., constJ : 1212))]
        const = ''
        if self.__lista_dict_cte is None: return ''
        for i in self.__lista_dict_cte:
            # Identificador de elemento.
            seguir = True
            ident = u'%s%s = {' % (self.indent, i[0])
            # print ident
            l_ident = " " * (len(ident.strip()))
            n_elem = len(i[1])
            p = -1
            for j in i[1]:
                p += 1
                if seguir:
                    ident += u'%s' % j
                    seguir = False
                    continue
                # Si es el último elemento, cerramos llave.
                if p == n_elem: 
                    ident += '}'
                else:
                    # Todavía hay elementos. Siguiente línea.
                    ident += u'%s%s%s%s%s' % (',', self.enter, l_ident, 
                                             self.indent, j)
            # Lo incluimos en las constantes.
            const += u'%s%s%s' % (ident, '}', self.enter)
        # Devolvemos lista de diccionarios.
        return const
    
    def estruct_cte(self):
        '''Devuelve todas las constantes'''
        aux1 = self.constantes()
        aux2 = self.dict_constantes()
        if len(aux1) != 0 and len(aux2) != 0:
            ret = u'%s%s%s' % (aux1, self.enter, aux2)
        elif len(aux1) != 0:
            ret = aux1
        else: ret = aux2
        # Devolvemos.
        if len(ret.strip()) == 0: return None
        aux = u'# Constantes de módulo.%s' % self.enter
        return aux + ret

class t_imports(t_traductor):
    '''Clase que genera código Python de imports''' 
    def __init__(self, lista_imports = None, lista_imports_from = None): 
        '''Constructor de clase'''        
        t_traductor.__init__(self)        
        self.__lista_imports = lista_imports
        self.__lista_imports_from = lista_imports_from
        
    def imports(self):
        '''Creación de imports'''
        # Creación de imports: ["import wx", "import datetime"]
        imports = ''
        if self.__lista_imports is None: imports = ''
        else:
            for i in self.__lista_imports:
                imports += u'%s%s' % (i, self.enter)
        imports_from = ''
        if self.__lista_imports_from is None: imports_from = ''
        else:
            for i in self.__lista_imports_from:
                imports_from += u'%s%s' % (i, self.enter)
        # Devolvemos texto de imports.
        return imports, imports_from

    def struct_import(self):
        '''Devuelve todos los imports'''
        # Obtenemos los imports.
        imp, imp_f = self.imports()
        if len(imp) != 0 and len(imp_f) != 0:
            ret = u'%s%s%s' % (imp, self.enter, imp_f)
        elif len(imp) != 0: ret = imp
        else: ret = imp_f
        # Devolvemos imports.
        if len(ret.strip()) == 0: return None
        aux = u'# Módulos a importar.%s' % self.enter
        return aux + ret

class t_clase(t_traductor):
    '''Clase que genera código Python de una clase''' 
    def __init__(self, indent, nombre, hereda_de = None,
                 max_column = MAX_COLUMN_INFO, coment = None, param_inst = None, 
                 atrib = None, prop = None, clases_externas = None, metodos = None,
                 codigo_constructor = tuple(), codigo_destructor = None, variables = None):
        '''Constructor de clase'''
        t_traductor.__init__(self)        
        self.indent = " " * indent
        # self.__codigo_init = codigo_init
        self.__nombre = nombre
        self.__hereda_de = hereda_de
        self.__max_column = max_column
        self.__coment = coment
        self.__param_inst = param_inst
        self.__atrib = atrib
        self.__prop = prop
        self.__metodos = metodos
        self.__clases_externas = clases_externas
        self.__codigo_constructor = codigo_constructor # 2-tupla (cons_ini, cons_fin)
        self.__codigo_destructor = codigo_destructor
        self.__variables = variables
        
    def variables(self):
        '''Constantes de clase'''
        # Creación de constantes: ["const1 = 'valor'", "const2 = 1"]
        const = '' # %s# Constantes de clase.%s' % (self.indent, self.enter)
        if self.__variables is not None:
            for i in self.__variables:
                const += u'%s%s%s' % (self.indent, i, self.enter)
        else: return None
        # Devolvemos texto de constantes.
        return const
    
    def constructor(self, posicion):
        '''Método para añadir código Python extra en el constructor'''
        codigo = ''
        if posicion == 'inicio': 
            indice = 0
            # codigo = u'%s# Código inicial del constructor.%s' % (self.indent * 2, self.enter)     
        if posicion == 'final': 
            indice = 1
            # codigo = u'%s# Código final del constructor.%s' % (self.indent * 2, self.enter)     
        if self.__codigo_constructor[indice] is not None:
            if len(self.__codigo_constructor) != 0: 
                # Hay que incluir la indentación.
                texto_aux = StringIO.StringIO(self.__codigo_constructor[indice])
                for i in texto_aux: 
                    codigo += u'%s%s' % (self.indent * 2, i)     
                return codigo
        # En cualquier otro caso, devolvemos nulo.
        return None

    def destructor(self):
        '''Método para añadir código Python en el destructor'''
        codigo = '' 
        if self.__codigo_destructor is not None: 
            # Hay que incluir la indentación.
            texto_aux = StringIO.StringIO(self.__codigo_destructor)
            for i in texto_aux: 
                codigo += u'%s%s' % (self.indent * 2, i)     
            # aux = '%s# Código del destructor.%s' % (self.indent, self.enter)
            cabecera = u'%sdef __del__(self):%s' % (self.indent, self.enter)
            total = cabecera + codigo
            return total
        else:
            return None
    
    def cabecera(self):
        '''Método para construir la cabecera de la clase'''
        # Identificador de elemento, esto es, una clase.
        # aux = u'# Definición de clase %s%s' % (self.__nombre, self.enter)
        ident = "class"
        # Si no hay clases de las que hereda, object por defecto.
        if self.__hereda_de is None:
            if self.__clases_externas is None:
                return u'%s %s(object):' % (ident, self.__nombre)
            else:
                return u'%s %s(%s):' % (ident, self.__nombre, self.__clases_externas.strip())
        # ¿De quién hereda esta clase?
        hereda_de = ''
        for i in self.__hereda_de:
            if len(hereda_de) == 0: hereda_de += i[0]
            else: hereda_de += u'%s%s' % (', ', i[0])
        # Creamos la cabecera.
        if self.__clases_externas is not None:
            hereda_de += u'%s%s' % (', ', self.__clases_externas.strip())
        cabecera = u'%s %s(%s):' % (ident, self.__nombre, hereda_de)
        # Devolvemos cabecera.
        return cabecera

    def init_clases_a_heredar(self):
        '''
        Método para construir las llamadas a los __init__ de las clases 
        de las que se hereda.
        '''
        if self.__hereda_de is None: return None
        inits = list()
        for i in self.__hereda_de:
            if i[1] is None:
                # No hay parámetros de instanciación.
                aux = u'%s.__init__(self)' % (i[0].strip()) # , self.enter)
            else:
                # Lleva parámetros de instanciación.
                param = ''
                for j in i[1]:
                    if len(param) == 0: param += j
                    else: param += u'%s%s' % (', ', j)
                # Formamos la cadena.
                aux = u'%s.__init__(self, %s)' % (i[0].strip(), param) # , self.enter)
            # Añadimos la inicialización en la lista.
            inits.append(aux)
        # Devolvemos el texto.
        ret = ''
        for i in inits: ret += u'%s%s%s' % (self.indent * 2, i, self.enter)
        return ret
    
    def metodo_init(self):
        '''Método para construir el método __init__ de la clase'''
        # Identificador de elemento.
        ident = u"def __init__(self"
        # Parámetros de la clase.
        parametros = ''
        if self.__param_inst is None: parametros = ''
        else:
            lista_param = []
            # param = [(nombre_param, valor_por_defecto), ...]
            for i in self.__param_inst:
                # Nombre del parámetro.
                nombre_param = i[0].strip()
                # Valor por defecto.
                if i[1] is not None: 
                    valor_por_defecto = i[1] 
                    # Creamos parámetro con inicialización.
                    k = u'%s = %s' % (nombre_param, valor_por_defecto)
                else:
                    # Creamos parámetro sin inicialización.
                    k = u'%s' % (nombre_param)
                # Añadimos el parámetro a la lista de parámetros.
                lista_param.append(k)
            # Creamos parámetros.
            for i in lista_param:
                # if len(parametros) == 0: parametros += '%s%s' % (', ', i)
                # else: parametros += '%s%s' % (', ',i)
                parametros += u'%s%s' % (', ',i)
        # [28-07-2011] Hay que incluir las posibles inicializaciones de propiedades al final.
        if self.__prop is not None:
            # param = [(nombre_prop, valor_por_defecto, información), ...]
            for i in self.__prop:
                # Nombre de la propiedad.
                nombre_prop = i[0].strip()
                # Valor predeterminado.
                valor = i[1] if i[1] is not None else ''
                valor = unicode(i[1]).strip()
                # Formamos el parámetro.
                if len(valor) == 0:
                    param = u'%s%s = None' % (', ',nombre_prop)
                else:
                    param = u'%s%s = %s' % (', ',nombre_prop, valor)
                # Incluimos el atributo.
                parametros += param
        # Formamos la cabecera del __init__ final.
        if len(parametros) == 0:
            ret = u'%s%s%s' % (self.indent, ident, '):')
        else:
            ret = u'%s%s%s):' % (self.indent, ident, parametros)
        # [04-08-2011] No se incluye init si no hay clases de las
        # que heredar y no hay ni atributos, ni propiedades, ni códigos.
        if self.__hereda_de is None and self.__atrib is None and \
           self.__prop is None and self.__codigo_constructor == (None, None): 
            if self.__param_inst is None:
                # [11-08-2011] Si no hay métodos, se incluye 'pass'.
                # [03-10-2011] Si no hay destructor ni constantes de clase, 
                #              se incluye 'pass'.
                if self.__metodos is None and self.__codigo_destructor is None \
                   and self.__variables is None:
                    return u'%s%s' % (self.indent, 'pass') 
                else:
                    # return '%s' % self.enter * 2
                    # [03-10-2011] Devolvemos nulo.
                    return None
            else:
                ret += u'%s%s%s' % (self.enter, self.indent * 2, 'pass')
        # Y la devolvemos.
        # aux = '%s# Código del constructor.%s' % (self.indent, self.enter)
        return ret

    def informacion(self):
        '''Método para construir la documentación de la clase'''        
        # Comentario.
        if self.__coment is not None:
            comentario = ''
            lineas = textwrap.wrap(self.__coment, self.__max_column)
            for i in lineas: comentario += u'%s%s%s' % (self.indent, i, self.enter)
            triple_comillas = "\'\'\'"
            ret = u'%s%s%s%s%s%s' % (self.indent, triple_comillas, self.enter, 
                                      comentario, self.indent, triple_comillas)
            # Devolvemos la documentación.
            return ret
        else: return None

    def atributos(self):
        '''Método para construir los atributos de la clase'''
        # Atributos de la clase.
        atributos = ''
        if self.__atrib is None: 
            return None
        else:
            lista_atrib = []
            # param = [(nombre_atrib, valor_por_defecto, oculto), ...]
            for i in self.__atrib:
                # Nombre del atributo.
                nombre_param = i[0].strip()
                # Valor por defecto.
                valor_por_defecto = i[1]
                # ¿Atributo oculto?
                es_oculto = i[2]
                # Formamos el atributo.
                if es_oculto:
                    nombre_param = u'%s%s' % ('__', nombre_param)
                if valor_por_defecto is not None:
                    nombre_param = u'%s = %s' % (nombre_param, valor_por_defecto)
                else:
                    nombre_param = u'%s = None' % (nombre_param)
                nombre_param = u'%sself.%s' % (self.indent * 2, nombre_param) 
                # Incluimos el atributo en la lista de atributos.
                lista_atrib.append(nombre_param)
            # Creamos atributos.
            for i in lista_atrib:
                atributos += u'%s%s' % (i, self.enter)
        # Y la devolvemos.
        # aux = '%s# Atributos de clase.%s' % (self.indent * 2, self.enter) 
        return atributos

    def propiedades1(self):
        '''Método para construir los atributos ocultos de las propiedades de la clase'''
        # Propiedades de la clase.
        propiedades = ''
        if self.__prop is None: 
            return None
        else:
            # param = [(nombre_prop, valor_por_defecto, información), ...]
            for i in self.__prop:
                # Nombre de la propiedad.
                nombre_prop = i[0].strip()
                # Formamos el atributo.
                atrib = u'%sself.__%s = %s%s' % (self.indent * 2, nombre_prop, nombre_prop, self.enter)
                # Incluimos el atributo.
                propiedades += atrib
        # Y la devolvemos.
        return propiedades
    
    def propiedades2(self):
        '''Método para construir los métodos de las propiedades de la clase'''
        # Propiedades de la clase.
        propiedades = ''
        if self.__prop is None: 
            return None
        else:
            # param = [(nombre_prop, valor_por_defecto, información), ...]
            for i in self.__prop:
                # Nombre de la propiedad.
                nombre_prop = i[0].strip()
                # Formamos el getter.
                getter = u'%sdef __get%s(self):%s' % (self.indent, nombre_prop.capitalize(), self.enter)
                getter += u'%sreturn self.__%s%s' % (self.indent * 2, nombre_prop, self.enter)
                # Formamos el setter.
                setter = u'%sdef __set%s(self, %s):%s' % (self.indent, nombre_prop.capitalize(),
                                                       nombre_prop, self.enter)
                setter += u'%sself.__%s = %s%s' % (self.indent * 2, nombre_prop, nombre_prop, self.enter)
                # Incluimos getter y setter de la propiedad.
                propiedades += getter
                propiedades += setter
        # Y la devolvemos.
        return propiedades

    def propiedades3(self):
        '''Método para construir la definición de las propiedades de la clase'''
        # Propiedades de la clase.
        propiedades = ''
        if self.__prop is None: 
            return None
        else:
            # param = [(nombre_prop, valor_por_defecto, información), ...]
            for i in self.__prop:
                # Nombre de la propiedad.
                nombre_prop = i[0].strip()
                # peso = property(fget = __getPeso, fset = __setPeso, doc = 'Peso')
                # Información de la propiedad.
                info = unicode(i[2]).strip()
                # Formamos el property.
                proper = u"%s%s = property(fget = __get%s, fset = __set%s, doc = u'%s')%s" % \
                       (self.indent, nombre_prop, nombre_prop.capitalize(), 
                        nombre_prop.capitalize(), info, self.enter)
                # Incluimos propiedad.
                propiedades += proper
        # Y la devolvemos.
        return propiedades
    
    def clase(self):
        '''Método que devuelve todo el código una clase completa'''
        # Formamos partes de la clase.
        cabecera = self.cabecera()
        informacion = self.informacion()
        init = self.metodo_init()
        inits = self.init_clases_a_heredar()
        atributos = self.atributos()
        # codigo_init = self.codigo_init()
        prop_atrib = self.propiedades1()
        prop_met = self.propiedades2()
        prop_prop = self.propiedades3()
        constructor_ini = self.constructor('inicio')
        constructor_fin = self.constructor('final')
        destructor = self.destructor()
        variables = self.variables()
        # Formamos clase con información en los bloques.
        texto = u'# Definición de clase %s.%s' % (self.__nombre, self.enter)
        texto += cabecera + self.enter
        if informacion is not None: texto += informacion + self.enter
        if variables is not None: 
            texto += u"%s# Constantes de clase.%s" % (self.indent, self.enter)
            texto += variables + self.enter
        if init is not None:
            if init.strip() == 'pass': texto += init + self.enter
            else:
                texto += u"%s# Método de código constructor.%s" % (self.indent, self.enter)
                texto += init + self.enter
        if inits is not None: 
            texto += u"%s# Llamada a los constructores padre.%s" % (self.indent * 2, self.enter)
            texto += inits + self.enter
        if constructor_ini is not None: 
            texto += u"%s# Código inicial del constructor.%s" % (self.indent * 2, self.enter)
            texto += constructor_ini + self.enter
        if prop_atrib is not None: 
            texto += u"%s# Atributos usados por propiedades.%s" % (self.indent * 2, self.enter)
            texto += prop_atrib + self.enter
        if atributos is not None: 
            texto += u"%s# Atributos de clase.%s" % (self.indent * 2, self.enter)
            texto += atributos + self.enter
        if constructor_fin is not None: 
            texto += u"%s# Código final del constructor.%s" % (self.indent * 2, self.enter)
            texto += constructor_fin + self.enter
        if prop_met is not None: 
            texto += u"%s# Getters y Setters de propiedades.%s" % (self.indent, self.enter)
            texto += prop_met + self.enter
        if prop_prop is not None: 
            texto += u"%s# Propiedades de clase.%s" % (self.indent, self.enter)
            texto += prop_prop + self.enter
        if destructor is not None: 
            texto += u"%s# Método destructor de clase.%s" % (self.indent, self.enter)
            texto += destructor + self.enter
        
        # Eliminamos las líneas vacías.
        texto = f_quitar_lineas_vacias(texto)
        # Devolvemos texto.
        return texto

class t_metodo(t_traductor):
    '''Clase que genera código Python de un método''' 
    def __init__(self, indent, nombre, max_column = MAX_COLUMN_INFO, 
                 info = None, param = None, codigo = None, oculto = False): 
        '''Constructor de clase'''
        t_traductor.__init__(self)        
        self.indent = " " * indent 
        self.__nombre = nombre
        self.__max_column = max_column
        self.__info = info
        self.__param = param # (param1, valor_ini, tipo_dato), ...
        self.__codigo = codigo
        self.__oculto = oculto
        
    def cabecera(self):
        '''Método para construir la cabecera del método'''
        # Identificador de elemento, esto es, un método.
        ident = 'def'
        # Parámetros del método.
        parametros = ''
        if self.__param is None: parametros = 'self'
        else:
            lista_param = []
            # param = [(nombre_param, valor_por_defecto|None, tipo_dato|None), ...]
            for i in self.__param:
                # Nombre del parámetro.
                nombre_param = i[0].strip()
                # Valor por defecto.
                if i[1] is not None: 
                    valor_por_defecto = i[1] 
                    # Creamos parámetro con inicialización.
                    k = u'%s = %s' % (nombre_param, valor_por_defecto)
                else:
                    # Creamos parámetro sin inicialización.
                    k = u'%s' % nombre_param
                # Añadimos el parámetro a la lista de parámetros.
                lista_param.append(k)
            # Creamos parámetros.
            for i in lista_param:
                if len(parametros) == 0: parametros += u'self, %s' % i
                else: parametros += u'%s%s' % (', ',i)
        # Comprobamos si el método es oculto.
        if self.__oculto:
            aux = u'__%s' % self.__nombre
        else:
            aux = self.__nombre
        # Formamos la cabecera final.
        ret = u'%s%s %s(%s):' % (self.indent, ident, aux, parametros)
        # Y la devolvemos.
        return ret

    def aserciones(self):
        '''Método para construir las aserciones de método'''
        # Identificador de elemento, esto es, un assert.
        ident = "assert"
        ret = ''
        # Buscamos aserciones (comprobar los tipos en los parámetros del método).
        if self.__param is not None: 
            lista_asserts = []
            # param = [(nombre_param, valor_por_defecto|None, tipo_dato|None), ...]
            for i in self.__param:
                # Nombre del parámetro.
                nombre_param = i[0]
                # Valor por defecto del parámetro.
                valor_por_defecto = i[1]
                # Comprobación de tipo de dato.
                tipo_dato = None if i[2] == '-' else i[2]
                if tipo_dato is not None: 
                    # tipo_dato = i[2]
                    # Creamos asercion.
                    if valor_por_defecto == 'None':
                        j = u'%sif %s is not None: assert(type(%s)==%s)' % \
                        (self.indent * 2, nombre_param, nombre_param, tipo_dato)
                    else: j = u'%sassert(type(%s)==%s)' % (self.indent * 2, nombre_param, tipo_dato)                    
                    # Lo añadimos a la lista de aserciones.
                    lista_asserts.append(j)
            # Formamos el conjunto de asserts final.
            for i in lista_asserts:
                ret += u'%s%s' % (i, self.enter)
        else: return None
        # Y lo devolvemos.
        return ret
    
    def cuerpo(self):
        '''Método para construir el cuerpo del método'''
        # Si hay código Python, será parte del cuerpo.
        codigo = ''
        if self.__codigo is not None:
            # Hay que incluir la indentación.
            texto_aux = StringIO.StringIO(self.__codigo)
            for i in texto_aux: 
                codigo += u'%s%s' % (self.indent * 2, i)
        # Si hay asserts, será parte del cuerpo.
        asserts = self.aserciones()
        # Si no hay asserts ni código Python extra, el cuerpo es 'pass'
        if codigo is None: codigo = ''
        if asserts is None: asserts = ''
        if len(codigo.strip()) == 0 and len(asserts.strip()) == 0:
            cuerpo = u'%s%s' % (self.indent * 2, 'pass')
        else:
            cuerpo = u'%s%s%s' % (asserts, self.enter, codigo)
        # Devolvemos el cuerpo.
        return cuerpo

    def informacion(self):
        '''Método para construir la documentación del método'''        
        # Comentario.
        if self.__info is not None:
            comentario = ''
            lineas = textwrap.wrap(self.__info, self.__max_column)
            for i in lineas: comentario += '%s%s%s' % (self.indent * 2, i, self.enter)
            triple_comillas = "\'\'\'"
            ret = u'%s%s%s%s%s%s' % (self.indent * 2, triple_comillas, self.enter, 
                                    comentario, self.indent * 2, triple_comillas)
        else: return None
        # Devolvemos la documentación.
        return ret
    
    def metodo(self):
        '''Método que devuelve el código del método'''
        ret = ''
        # Datos del método.
        cabecera = self.cabecera()
        informacion = self.informacion()
        cuerpo = self.cuerpo()
        # Formamos el bloque.
        ret = u'%s# Método de clase.%s' % (self.indent, self.enter)
        ret += cabecera + self.enter
        if informacion is not None:
            ret += informacion + self.enter
        ret += cuerpo + self.enter
        # Eliminamos las líneas vacías.
        ret = f_quitar_lineas_vacias(ret)
        # Devolvemos texto.
        return ret

class t_estructura(t_traductor):
    '''Clase que genera código Python para emular una estructura de registro''' 
    def __init__(self, indent, lista_estruc = None): 
        '''Constructor de clase'''
        t_traductor.__init__(self)
        self.indent = " " * indent
        self.__lista_estruc = lista_estruc
        
    def asignaciones(self, nombre, campos):
        '''Crea los campos del registro'''
        ret = ''
        for i in campos:
            valor = u'%s.%s = %s' % (nombre, i[0].strip(), i[1])
            ret += u'%s%s' % (valor, self.enter)
        return ret
            
    def estructuras(self):
        '''Crea la estructura del registro'''
        # Creación de estructuras: 
        # [(estruc1, emulacion, ([campo1, 'valor'], ..., [campoK, 100])),
        # ...,
        # (estrucN, emulacion, ([campoL, 'None'], ..., [constJ = 1212]))]
        estruc = ''
        if self.__lista_estruc is None: return ''
        for i in self.__lista_estruc:
            # Valores.
            nombre = unicode(i[0]).strip()
            emular = i[1]
            campos = i[2]
            # Creamos identificador.
            if emular == u'Class':
                ident = u'class %s(object):%s%spass%s' % (nombre, self.enter, self.indent, self.enter * 2)
            if emular == u'Function':
                ident = u'def %s():%s%spass%s' % (nombre, self.enter, self.indent, self.enter * 2)
            campos_estruc = self.asignaciones(nombre, campos)
            # Incluimos los datos.
            estruc += u'%s%s%s' % (ident, campos_estruc, self.enter)
        # Devolvemos estructuras.
        if len(estruc.strip()) == 0: return None
        aux = u'# Registros de módulo.%s' % self.enter
        return aux + estruc
